from collections import defaultdict
import math
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import grid
from api import rpc_make, unmarshal
import json
import matplotlib.animation as animation
from matplotlib.path import Path
import matplotlib.patches as patches
from color import *


class Board:
    def __init__(self):
        fig, ax = plt.subplots(figsize=(9, 16))
        fig.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95)
        ax.set_aspect("equal")
        ax.set_xticks([])
        ax.set_yticks([])
        ax.axis("off")
        ax.set_xlim(0, 9)
        ax.set_ylim(0, 16)

        self.grid_size = 8
        self.x_start = (9 - self.grid_size) / 2
        self.y_start = (16 - self.grid_size) / 2 + 1
        self.cell_size = self.grid_size / 9
        self.cand_size = self.cell_size / 3

        self.fig = fig
        self.ax = ax
        self.picture_num = 0

        self.grid = grid.Grid()
        plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]  # Mac系统常用中文字体

        self.font35 = FontProperties(fname="AlibabaPuHuiTi-3-55-Regular.ttf")
        self.font55 = FontProperties(fname="AlibabaPuHuiTi-3-55-Regular.ttf")
        self.font85 = FontProperties(fname="AlibabaPuHuiTi-3-55-Regular.ttf")

        self.heading = ""
        self.comment = ""

    def set_heading(self, heading):
        self.heading = heading

    def drawHeading(self):
        self.ax.text(
            4.5,
            self.y_start + self.grid_size + 1.5,
            self.heading,
            fontproperties=self.font85,
            fontsize=32,
            ha="center",
            va="center",
        )

    def set_comment(self, comment):
        self.comment = comment

    def drawComment(self):
        self.ax.text(
            4.5,
            self.y_start - 1.5,
            self.comment,
            fontproperties=self.font35,
            fontsize=16,
            ha="center",
            va="center",
        )

    def drawFrame(self):

        for i in range(10):
            # 水平线
            self.ax.plot(
                [self.x_start, self.x_start + self.grid_size],
                [
                    self.y_start + i * self.grid_size / 9,
                    self.y_start + i * self.grid_size / 9,
                ],
                color="black",
                linewidth=3 if i % 3 == 0 else 1,
            )
            # 垂直线
            self.ax.plot(
                [
                    self.x_start + i * self.grid_size / 9,
                    self.x_start + i * self.grid_size / 9,
                ],
                [self.y_start, self.y_start + self.grid_size],
                color="black",
                linewidth=3 if i % 3 == 0 else 1,
            )

        # 添加行列号标注
        for i in range(1, 10):
            # 左侧行号
            self.ax.text(
                self.x_start - 0.3,
                self.y_start + (9 - i) * self.grid_size / 9 + self.grid_size / 18,
                str(i),
                fontsize=12,
                ha="center",
                va="center",
                color="black",
            )
            # 右侧行号
            self.ax.text(
                self.x_start + self.grid_size + 0.3,
                self.y_start + (9 - i) * self.grid_size / 9 + self.grid_size / 18,
                str(i),
                fontsize=12,
                ha="center",
                va="center",
                color="black",
            )
            # 上方列号
            self.ax.text(
                self.x_start + (i - 0.5) * self.grid_size / 9,
                self.y_start + self.grid_size + 0.3,
                str(i),
                fontsize=12,
                ha="center",
                va="center",
                color="black",
            )
            # 下方列号
            self.ax.text(
                self.x_start + (i - 0.5) * self.grid_size / 9,
                self.y_start - 0.3,
                str(i),
                fontsize=12,
                ha="center",
                va="center",
                color="black",
            )

    def drawGrid(self):
        """在指定位置绘制数独格子"""
        for row in self.grid.rows:
            for cell in row:
                x = self.x_start + (cell.colNum) * self.grid_size / 9
                y = self.y_start + (8 - cell.rowNum) * self.grid_size / 9

                if cell.value != 0:
                    # 填写数字值
                    color = givenColor if cell.given else "black"
                    self.ax.text(
                        x + self.grid_size / 18,
                        y + 0.9 * self.grid_size / 18,
                        str(cell.value),
                        fontsize=36,
                        ha="center",
                        va="center",
                        color=color,
                    )
                else:
                    # continue
                    # 填写候选数字
                    for cand in cell.cands:
                        sub_x = (cand - 1) % 3 + 1 / 2
                        sub_y = 2 - (cand - 1) // 3 + 1 / 2
                        self.ax.text(
                            x + (sub_x + 0.05) * self.grid_size / 9 / 3 * 0.9,
                            y + (sub_y + 0.05) * self.grid_size / 9 / 3 * 0.9,
                            str(cand),
                            fontsize=16,
                            ha="center",
                            va="center",
                            color="black",
                        )

    def save(self, filename="test.png"):
        plt.savefig(filename, dpi=150, bbox_inches="tight", pad_inches=0.1)
        plt.close()

    def draw_block(self, rowNum, colNum, color):
        x = self.x_start + (colNum) * self.grid_size / 9
        y = self.y_start + (8 - rowNum) * self.grid_size / 9

        rect = plt.Rectangle(
            (x, y),
            self.grid_size / 9,
            self.grid_size / 9,
            facecolor=color,
            alpha=1.0,
            edgecolor="none",
        )
        self.ax.add_patch(rect)

    def draw_big_circle(self, rowNum, colNum, color):
        x = self.x_start + (colNum + 1 / 2) * self.grid_size / 9
        y = self.y_start + (8 - rowNum + 1 / 2) * self.grid_size / 9

        rect = plt.circle(
            (x, y),
            self.grid_size / 18,
            color=color,
            alpha=1.0,
            edgecolor="none",
        )
        self.ax.add_patch(rect)

    def draw_circle(self, rowNum, colNum, value, color):
        sub_x = (value - 1) % 3 + 1 / 2
        sub_y = 2 - (value - 1) // 3 + 1 / 2
        x = self.x_start + (colNum) * self.grid_size / 9
        y = self.y_start + (8 - rowNum) * self.grid_size / 9

        x = x + (sub_x + 0.05) * self.grid_size / 9 / 3 * 0.9
        y = y + (sub_y + 0.05) * self.grid_size / 9 / 3 * 0.9

        radius = self.grid_size / 9 / 3 * 0.9 / 2
        circle = plt.Circle((x, y), radius, color=color, alpha=1.0)
        self.ax.add_patch(circle)

    def draw_prompt(self, result):
        blockColors = defaultdict(list)
        if result.helphouses:
            for i, hs in enumerate(result.helphouses):
                for h in hs:
                    if h.kind == "R":
                        cs = self.grid.rows[h.num]
                    elif h.kind == "C":
                        cs = self.grid.cols[h.num]
                    else:
                        cs = self.grid.boxes[h.num]
                    # print("cs:", cs)
                    for c in cs:
                        blockColors[(c.rowNum, c.colNum)].append(HelpCellsColors[i])
            # print("choose", c)
        if result.helpcells:
            for i, hc in enumerate(result.helpcells):
                for c in hc:
                    # self.draw_block(c.rownum, c.colnum, HelpCellsColors[i])
                    blockColors[(c.rownum, c.colnum)].append(HelpCellsColors[i])

        for b, cs in blockColors.items():
            color = cs[0]
            for c in cs[1:]:
                color = blend_colors(color, c, 0.5)
            self.draw_block(b[0], b[1], color)

        for i, cs in enumerate(result.helpcands):
            for c in cs:
                for value in c.cands:
                    # circle = self.baseCells[c.rownum][c.colnum].candCircles[value - 1]
                    # color = HelpCandsColors[i]
                    self.draw_circle(c.rownum, c.colnum, value, HelpCandsColors[i])

        for i, hv in enumerate(result.helpvalues):
            for v in hv:
                # print("helpvalues", v)
                self.draw_big_circle(c.rownum, c.colnum, HelpCandsColors[i])

        for i, chain in enumerate(result.helplinks):
            self.draw_chain(chain)

        if result.chosen:
            c = result.chosen
            self.draw_block(c.rownum, c.colnum, chosenBg)
            self.draw_circle(c.rownum, c.colnum, c.value, chosenColor)

        for c in result.removed:
            for value in c.cands:
                self.draw_circle(c.rownum, c.colnum, value, removeColor)

    def draw_node_frame(self, frame, linewidth=2, color=groupColor, alpha=0.7):
        if isinstance(color, str):
            # 如果是颜色名称，转换为RGBA并调整
            from matplotlib.colors import to_rgba

            r, g, b, _ = to_rgba(color)
            # 加深颜色(减少亮度)
            color = (r * alpha, g * alpha, b * alpha, alpha)  # alpha=0.7
        else:
            # 如果是RGBA元组，直接调整
            color = (color[0] * alpha, color[1] * alpha, color[2] * alpha, alpha)

        x1, y1, x2, y2 = frame
        rect = plt.Rectangle(
            (x1, y1),
            x2 - x1,  # width
            y2 - y1,  # height
            linewidth=linewidth,
            edgecolor=color,
            facecolor="none",  # transparent fill
            alpha=alpha,  # transparency
        )
        self.ax.add_patch(rect)

    def draw_node_frames(self, chain):
        self.nodeFrames = [None] * len(chain.rnodes)
        for i, node in enumerate(chain.rnodes):
            if node.kind == "single":
                continue
            frame = self.get_group_frame(node)
            self.nodeFrames[i] = frame
            self.draw_node_frame(frame)

    def draw_chain(self, chain, factor=1):
        self.draw_chain_cands(chain)
        self.draw_chain_links(chain)
        self.draw_node_frames(chain)

        return

    def draw_chain_links(self, chain):
        for i in range(len(chain.rnodes) - 1):
            frame1 = None
            frame2 = None
            node1 = chain.rnodes[i]
            node2 = chain.rnodes[i + 1]
            if node1.kind == "single":
                x1, y1 = self.get_cand_center(
                    node1.rcells[0].rownum, node1.rcells[0].colnum, node1.cand
                )
            else:
                frame1 = self.get_group_frame(node1)
            if node2.kind == "single":
                x2, y2 = self.get_cand_center(
                    node2.rcells[0].rownum, node2.rcells[0].colnum, node2.cand
                )
            else:
                frame2 = self.get_group_frame(node2)

            if frame1 and frame2:
                x1, y1 = self.get_frame_link_point(
                    (frame2[2] + frame2[0]) / 2, (frame2[3] + frame2[1]) / 2, frame1
                )
                x2, y2 = self.get_frame_link_point(
                    (frame1[2] + frame1[0]) / 2, (frame1[3] + frame1[1]) / 2, frame2
                )
            elif frame1:
                x1, y1 = self.get_frame_link_point(x2, y2, frame1)
                print(x2, y2, x1, y1, "frame1")
            elif frame2:
                x2, y2 = self.get_frame_link_point(x1, y1, frame2)
                print(x2, y2, x1, y1, "frame2")

            if chain.links[i] == "=":
                color = strongColor
                line_width = 3
                dash = None
            else:
                color = weakColor
                line_width = 2
                dash = (5, 2)
            self.draw_links(x1, y1, x2, y2, color, line_width, dash)

    def draw_links(
        self, x1, y1, x2, y2, color="black", linewidth=3, dashPattern=(5, 2)
    ):
        # 计算两点距离
        length = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

        # 当x1=x2时（垂直线），控制点在水平方向偏移
        if x1 == x2:
            ctrl_x = (x1 + x2) / 2 + length * 0.3
            ctrl_y = (y1 + y2) / 2
        # 当y1=y2时（水平线），控制点在垂直方向偏移
        elif y1 == y2:
            ctrl_x = (x1 + x2) / 2
            ctrl_y = (y1 + y2) / 2 + length * 0.3
        # 其他情况（斜线），控制点在垂直方向偏移
        else:
            ctrl_x = (x1 + x2) / 2
            ctrl_y = (y1 + y2) / 2 + abs(x2 - x1) * 0.3

        path = Path(
            [(x1, y1), (ctrl_x, ctrl_y), (x2, y2)],
            [Path.MOVETO, Path.CURVE3, Path.CURVE3],
        )

        # 增加透明度(alpha=0.7)并加深颜色
        if isinstance(color, str):
            # 如果是颜色名称，转换为RGBA并调整
            from matplotlib.colors import to_rgba

            r, g, b, _ = to_rgba(color)
            # 加深颜色(减少亮度)
            color = (r * 0.7, g * 0.7, b * 0.7, 0.7)  # alpha=0.7
        else:
            # 如果是RGBA元组，直接调整
            color = (color[0] * 0.7, color[1] * 0.7, color[2] * 0.7, 0.7)

        patch = patches.PathPatch(
            path,
            fill=False,
            edgecolor=color,
            linewidth=linewidth,
            linestyle=(0, dashPattern),
        )
        self.ax.add_patch(patch)

    def draw_chain_cands(self, chain):
        if chain.links[0] == "=":
            color = linkRedColor
        else:
            color = linkGreenColor
        for node in chain.rnodes:
            for cell in node.rcells:
                if not cell:
                    break
                self.draw_circle(cell.rownum, cell.colnum, node.cand, color)
            if color == linkRedColor:
                color = linkGreenColor
            else:
                color = linkRedColor

    def calculate_control_points(self, x1, y1, x2, y2, factor=1):
        mid_x = (x1 + x2) / 2
        mid_y = (y1 + y2) / 2

        dx = x2 - x1
        dy = y2 - y1

        print(x1, y1, x2, y2, dx, dy)

        length = math.sqrt(dx**2 + dy**2)
        # rate = 1 - 0.2 * ((length / self.cellSize) ** 0.2)
        ul = length / self.cellSize * 3
        if ul < 1.5:
            rate = 1.5
        elif ul < 4.5:
            rate = 0.4
        elif ul < 7.5:
            rate = 0.3
        else:
            rate = 0.2
        # print(ul, rate)

        rate = -rate * factor

        offset = length * rate

        perp_dx = -dy / length
        perp_dy = dx / length

        # cx = mid_x - perp_dx * offset
        # cy = mid_y - perp_dy * offset
        cx = mid_x + perp_dx * offset
        cy = mid_y + perp_dy * offset

        return cx, cy

    def get_cand_center(self, rowNum, colNum, value):
        sub_x = (value - 1) % 3 + 1 / 2
        sub_y = 2 - (value - 1) // 3 + 1 / 2
        x = self.x_start + (colNum) * self.cell_size
        y = self.y_start + (8 - rowNum) * self.cell_size

        x = x + (sub_x + 0.05) * self.cand_size * 0.9
        y = y + (sub_y + 0.05) * self.cand_size * 0.9
        return x, y

    def get_cell_start_position(self, rowNum, colNum):
        x = self.x_start + colNum * self.cell_size
        y = self.y_start + (8 - rowNum) * self.cell_size
        return x, y

    def get_group_frame(self, node):
        if node.kind == "rowgroup":
            return self.get_group_row_frame(node)
        elif node.kind == "colgroup":
            return self.get_group_col_frame(node)
        elif node.kind == "boxgroup":
            return self.get_group_box_frame(node)

    def get_group_row_frame(self, node):
        minColNum = 9
        maxColNum = 0
        for cell in node.rcells:
            if cell.colnum > maxColNum:
                maxColNum = cell.colnum
            if cell.colnum < minColNum:
                minColNum = cell.colnum
        print(minColNum, maxColNum, "minColNum, maxColNum", node.rcells)
        x, y = self.get_cell_start_position(node.rcells[0].rownum, minColNum)
        left = x + (node.cand - 1) % 3 * self.cand_size
        top = y + (3 - (node.cand - 1) // 3) * self.cand_size
        x, y = self.get_cell_start_position(node.rcells[0].rownum, maxColNum)
        right = x + (node.cand - 1) % 3 * self.cand_size + self.cand_size
        bottom = y + (2 - (node.cand - 1) // 3) * self.cand_size
        # print(node.cand, "node.cand")
        return left, top, right, bottom

    def get_group_col_frame(self, node):
        minRowNum = 9
        maxRowNum = 0
        for cell in node.rcells:
            if cell.rownum > maxRowNum:
                maxRowNum = cell.rownum
            elif cell.rownum < minRowNum:
                minRowNum = cell.colnum
        x, y = self.get_cell_start_position(minRowNum, node.rcells[0].colnum)
        left = x + (node.cand - 1) % 3 * self.cand_size
        top = y + (3 - (node.cand - 1) // 3) * self.cand_size
        x, y = self.get_cell_start_position(maxRowNum, node.rcells[0].colnum)
        right = x + (node.cand - 1) % 3 * self.cand_size + self.cand_size
        bottom = y + (2 - (node.cand - 1) // 3) * self.cand_size
        return left, top, right, bottom

    def get_group_box_frame(self, node):
        minColNum = 9
        maxColNum = 0
        for cell in node.rcells:
            if cell.colnum > maxColNum:
                maxColNum = cell.colnum
            elif cell.colnum < minColNum:
                minColNum = cell.colnum

        minRowNum = 9
        maxRowNum = 0
        for cell in node.rcells:
            if cell.rownum > maxRowNum:
                maxRowNum = cell.rownum
            elif cell.rownum < minRowNum:
                minColNum = cell.colnum

        print("minmax:", minRowNum, maxRowNum, minColNum, maxColNum)
        x, y = self.get_cell_start_position(minRowNum, minColNum)
        left = x + (node.cand - 1) % 3 * self.cand_size
        top = y + (3 - (node.cand - 1) // 3) * self.cand_size
        x, y = self.get_cell_start_position(maxRowNum, maxColNum)
        right = x + (node.cand - 1) % 3 * self.cand_size + self.cand_size
        bottom = y + (2 - (node.cand - 1) // 3) * self.cand_size
        return left, top, right, bottom

    def get_frame_link_point(self, x, y, frame):
        left, top, right, bottom = frame
        center_x = (left + right) / 2
        center_y = (top + bottom) / 2
        a = math.atan((top - center_y) / (right - center_x))
        b = math.atan2((y - center_y), (x - center_x))
        if -a <= b < a:
            return right, center_y
        elif a <= b < math.pi - a:
            return center_x, top
        elif (math.pi - a <= b < math.pi) or (-math.pi <= b < -math.pi + a):
            return left, center_y
        elif -math.pi + a < b <= -a:
            return center_x, bottom
        else:
            return center_x, center_y

    """
    def get_cand_frame(self, value, node):
        top = 3
        bottom = 1
        left = 3
        right = 1
        for cell in node.cells:
            if cell.rownum ==     

    def drawLinkCands(self, link):
        headCircles = set()
        for cell in link.rnodes[0].rcells:
            if not cell:
                break
            circle = self.baseCells[cell.rownum][cell.colnum].candCircles[
                link.rnodes[0].cand - 1
            ]
            headCircles.add(circle)

        if link.chains[0] == "=":
            color = linkRedColor
        else:
            color = linkGreenColor
        for node in link.rnodes[:-1]:
            for cell in node.rcells:
                if not cell:
                    break
                circle = self.baseCells[cell.rownum][cell.colnum].candCircles[
                    node.cand - 1
                ]
                self.canvas.itemconfig(circle, fill=color, state="normal")
                self.hiddenToNormals.append(circle)
            if color == linkRedColor:
                color = linkGreenColor
            else:
                color = linkRedColor
        for cell in link.rnodes[-1].rcells:
            if not cell:
                break
            bc = self.baseCells[cell.rownum][cell.colnum]
            circle = bc.candCircles[link.rnodes[-1].cand - 1]
            if circle in headCircles:
                i = link.rnodes[-1].cand - 1
                c = self.canvas.create_oval(
                    bc.x0 + i % 3 * self.cellSize // 3 + 1,
                    bc.y0 + i // 3 * self.cellSize // 3 + 1,
                    bc.x0 + (i % 3 + 1) * self.cellSize // 3 - 1,
                    bc.y0 + (i // 3 + 1) * self.cellSize // 3 - 1,
                    fill="",
                    outline=color,
                    width=4,
                    # state="hidden",
                )
                self.lines.append(c)
            else:
                self.canvas.itemconfig(circle, fill=color, state="normal")
                self.hiddenToNormals.append(circle)

            # print("drawLinkCands", cell)


    def drawLink(self, link, factor=1):
        self.drawLinkCands(link)
        for node in link.rnodes:
            self.drawNodeFrame(node)
        for i in range(len(link.rnodes) - 1):
            node1 = link.rnodes[i]
            node2 = link.rnodes[i + 1]
            self.drawNodeFrame(node1)
            x1, y1 = self.getNodeCenter(node1)
            x2, y2 = self.getNodeCenter(node2)
            # dx = x2 - x1
            # dy = y2 - y1
            # curve_offset = self.cellSize // 4 + int(math.sqrt(dx * dx + dy * dy) * 0.1)
            # mid_x = x1 + dx / 2 + (curve_offset if i % 2 == 0 else -curve_offset)
            # mid_y = y1 + dy / 2 + (curve_offset if i % 2 == 0 else -curve_offset)
            mid_x, mid_y = self.calculate_control_points(x1, y1, x2, y2, factor)
            if mid_x < 0:
                mid_x = 0
            if mid_x > self.canvas.winfo_width():
                mid_x = self.canvas.winfo_width()
            if mid_y < 0:
                mid_y = 0
            if mid_y > self.canvas.winfo_height():
                mid_y = self.canvas.winfo_height()

            if link.chains[i] == "=":
                color = strongColor
                line_width = 3
                dash = None
            else:
                color = weakColor
                line_width = 2
                dash = (5, 2)

            line = self.canvas.create_line(
                x1,
                y1,
                mid_x,
                mid_y,
                x2,
                y2,
                smooth=True,
                fill=color,
                width=line_width,
                arrow=tk.LAST,
                dash=dash,
            )
            self.lines.append(line)

            for cell in node1.rcells:
                r, c, cand = cell.rownum, cell.colnum, node1.cand
                digit = self.baseCells[r][c].textCands[cand - 1]
                self.canvas.tag_raise(digit)
            for cell in node2.rcells:
                r, c, cand = cell.rownum, cell.colnum, node2.cand
                digit = self.baseCells[r][c].textCands[cand - 1]
                self.canvas.tag_raise(digit)

        for i, hv in enumerate(rresult.helpvalues):
            print("helpvalues", hv)
            for v in hv:
                # print("helpvalues", v)
                circle = self.baseCells[v.rownum][v.colnum].valueCircle
                color = HelpCandsColors[i]
                self.canvas.itemconfig(circle, fill=color, state="normal")
                self.hiddenToNormals.append(circle)

        if rresult.chosen:
            c = rresult.chosen
            # print("choose", c)
            circle = self.baseCells[c.rownum][c.colnum].candCircles[c.value - 1]
            self.canvas.itemconfig(circle, fill=chosenColor, state="normal")
            self.hiddenToNormals.append(circle)

            block = self.baseCells[c.rownum][c.colnum].block
            if rresult.strategyname == "Guess":
                self.canvas.itemconfig(block, fill=guessBg, state="normal")
            else:
                self.canvas.itemconfig(block, fill=chosenBg, state="normal")
            self.hiddenToNormals.append(block)

        for i, link in enumerate(rresult.helplinks):
            self.drawLink(link)

        for c in rresult.removed:
            # print("remove", c)
            for value in c.cands:
                circle = self.baseCells[c.rownum][c.colnum].candCircles[value - 1]
                self.canvas.itemconfig(circle, fill=removeColor, state="normal")
                self.hiddenToNormals.append(circle)

    """


def make(grid):
    resp = rpc_make(grid.encode())

    error = resp["error"]
    if error:
        print(error)
        return None

    results = json.loads(resp["result"])

    return results


def main():
    s = """
017002560034015080009000140450000600020781090008000021096000200080470950045200810
"""
    b = Board()
    b.grid.decode(s)
    b.grid.init()
    b.grid.clearup()

    b.set_heading("数独解题动画，入门级")

    resp = rpc_make(b.grid.encode())

    error = resp["error"]
    if error:
        print(error)
        return

    results = json.loads(resp["result"])
    print(results[0])

    # 创建动画更新函数
    def update(frame):
        # print("update", frame)
        b.ax.clear()
        b.ax.axis("off")

        result = json.loads(results[frame // 2])
        result = unmarshal(result)
        if frame % 2 == 0:
            b.set_comment(result.strategyname)
            print(frame)
            b.draw_prompt(result)
            b.drawGrid()
            b.drawHeading()
            b.drawFrame()
            b.drawComment()
        else:
            if result.chosen:
                chosen = result.chosen
                b.grid.solveOne(chosen.rownum, chosen.colnum, chosen.value)
            if result.removed:
                for c in result.removed:
                    for value in c.cands:
                        b.grid.removeCand(c.rownum, c.colnum, value)
            b.set_comment(result.strategyname)

            b.drawGrid()
            b.drawHeading()
            b.drawFrame()
            b.drawComment()
        return b.ax.artists  # 返回所有图形元素

    # 创建动画
    ani = animation.FuncAnimation(
        b.fig,
        update,
        frames=len(results) * 2,
        interval=800,  # 每帧间隔500ms
        blit=False,
        repeat=False,
    )

    # 保存为GIF
    try:
        writer = animation.FFMpegWriter(fps=1.25)  # 使用FFMpegWriter
        ani.save(
            "solution.mp4",  # 修改为mp4后缀
            writer=writer,
            dpi=160,
            savefig_kwargs={
                "pad_inches": 0.0,
                "facecolor": b.fig.get_facecolor(),
            },
        )
    except Exception as e:
        print(f"Could not save animation: {e}")


def paint():
    s = """
[23468]1[2345] [568][5689][25] [379][348][4789] 
7[238]9 4[18][12] [13]65 
[468][458][45] 7[15689]3 2[148][1489] 

56[237] 1[23]4 89[27] 
9[34]1 [358][2358]7 6[45][24] 
[24][247]8 9[25]6 [157][145]3 

[348][34578]6 2[13457]9 [135][1358][18] 
19[357] [35][357]8 426 
[2348][23458][2345] [356][13456][15] [1359]7[189] 

"""
    b = Board()
    b.grid.decode(s)
    b.grid.init()
    b.grid.clearup()

    b.set_heading("数独解题动画，题目由端庄微风PM提供")

    result = '{"chosen":"","removed":["R1C3[5]"],"helphouses":null,"helpcells":null,"helpvalues":null,"helpcands":null,"helplinks":["R1C6[5]=R9C6[5]-R8C45[5]=R8C3[5]"],"strategyname":"AIC With Group"}'

    result = json.loads(result)
    result = unmarshal(result)
    b.draw_prompt(result)

    b.drawGrid()
    b.drawHeading()
    b.drawFrame()
    b.drawComment()

    b.save("test.png")


if __name__ == "__main__":
    main()
    # paint()
